// (c) Copyright 2013 Mike Riedel.
// This source is subject to the Microsoft Public License (Ms-PL).
// All other rights reserved.

using System;

namespace FreshMeat.ErrorHandlingBlock
{
   /// <summary>
   /// A retry play that waits a linearly increasing amount of time between retries.
   /// </summary>
   //[DebuggerNonUserCode]
   public class LinearRetryPlan : IRetryPlan
   {
      readonly int maxAttempts;
      readonly TimeSpan firstWait;
      readonly TimeSpan increase;
      readonly TimeSpan maxWait;

      /// <summary>
      /// Creates instance using specified parameters.
      /// </summary>
      /// <param name="maxAttempts">Max attempts.</param>
      /// <param name="firstWait">Wait after first attempt.</param>
      /// <param name="increase">How much to increase the wait for each subsequent attempt, limited to maxWait.</param>
      /// <param name="maxWait">Max wait.</param>
      public LinearRetryPlan(int maxAttempts, TimeSpan firstWait, TimeSpan increase, TimeSpan maxWait)
      {
         this.maxAttempts = maxAttempts;
         this.firstWait = firstWait;
         this.increase = increase;
         this.maxWait = maxWait;
      }

      /// <summary>
      /// Determine whether to retry based on the number of attempts.
      /// </summary>
      /// <param name="attempts">The number of attempts so far, including the initial attempt.</param>
      /// <returns>True to retry, false to fail.</returns>
      public bool ShouldRetry(int attempts)
      {
         return attempts < maxAttempts;
      }

      /// <summary>
      /// Gets the time until the next attempt should be made.
      /// </summary>
      /// <param name="attempts">The number of attempts so far, including the initial attempt.</param>
      /// <returns>Time until the next attempt should be made.</returns>
      public TimeSpan GetTimeUntilNextAttempt(int attempts)
      {
         if (attempts <= 1) 
            return firstWait;

         var ticks = firstWait.Ticks + ((attempts - 1) * increase.Ticks);
         if (ticks > maxWait.Ticks)
            ticks = maxWait.Ticks;

         return TimeSpan.FromTicks(ticks);
      }
   }
}